import { ENUM_CLIP, ENUM_STATUS, ENUM_UI } from "../GbzEnum";
import GbzInstance from "../GbzInstance";
import { countGameLevelData, getDistance, getNumFromStr, random, takeValueFromArray, toXY } from "../GbzUtils";
import GbzAudioManager from "../manager/GbzAudioManager";
import GbzDataManager from "../manager/GbzDataManager";
import GbzPoolManager from "../manager/GbzPoolManager";
import GbzResManager from "../manager/GbzResManager";
import GbzSdkManager from "../manager/GbzSdkManager";
import GbzToastManager from "../manager/GbzToastManager";
import GbzBoard from "./GbzBoard";
import GbzBox from "./GbzBox";
import GbzScrew from "./GbzScrew";
import GbzTarget from "./GbzTarget";

// carolsail
const { ccclass, property } = cc._decorator;

@ccclass
export default class GbzLevel extends cc.Component {

    rootBoxes: cc.Node = null
    rootBoards: cc.Node = null
    targets: cc.Node = null

    screws: cc.Node[] = []
    boards: cc.Node[] = []

    boxDataArr: number[] = []

    screwRefreshArr: number[] = []

    init() {
        this.node.height = cc.winSize.height
        this.rootBoxes = this.node.getChildByName('root_boxes')
        this.rootBoards = this.node.getChildByName('root_boards')
        this.targets = this.node.getChildByName('targets')

        const json = GbzResManager.ins.json(`gbz_level${GbzDataManager.ins.game_level_current}`)
        const levelData = JSON.parse(JSON.stringify(json))
        if (levelData.groupData.length) {
            this.initData(levelData)
        }
        this.rootBoards.children.forEach(group => {
            group.active = true
            const groupName = group.name
            group.children.forEach(tempBoard => {
                tempBoard.active = false
                const boardIndex = getNumFromStr(tempBoard.name)
                const boardItem = GbzPoolManager.ins.getNode('GbzBoardItem', group)
                boardItem.getChildByName('screws').zIndex = 5
                const board = GbzPoolManager.ins.getNode(`GbzBoard${boardIndex}`, boardItem, cc.v3(tempBoard.x, tempBoard.y))
                board.angle = tempBoard.angle
                board.group = groupName
                board.getComponent(cc.PhysicsCollider).apply()
                const screws = boardItem.getChildByName('screws')
                const boardComp = board.getComponent(GbzBoard)
                const joints = boardComp.getJoints()
                joints.forEach(joint => {
                    if (joint.node.active) {
                        const group = joint.node.group
                        const groupDataArr = levelData['boards'][group]
                        if (groupDataArr && groupDataArr.length) {
                            const anchor = joint.anchor
                            const pos = toXY(joint.node, screws, anchor)
                            const screw = GbzPoolManager.ins.getNode('GbzScrew', screws, cc.v3(pos.x, pos.y))
                            const screwComp = screw.getComponent(GbzScrew)
                            const randomIndex = random(0, groupDataArr.length - 1)
                            const groudData = groupDataArr[randomIndex]
                            groupDataArr.splice(randomIndex, 1)
                            screwComp.init(groudData, group, joint)
                            joint.connectedBody = screw.getComponent(cc.RigidBody)
                            this.screws.push(screw)
                        }
                    }
                })
                this.boards.push(board)
            })
        })

        // this.rootBoards.children.forEach(gboard => {
        // gboard.children.forEach(item => {
        //     const item1 = item.children[0]
        //     const item2 = item.children[1]
        //     let board = item1
        //     let screws = item2
        //     if (item2.getComponent(cc.RigidBody)) {
        //         board = item2
        //         screws = item1
        //     }
        //     board.group = gboard.name
        //     board.getComponent(cc.PhysicsCollider).apply()

        //     const boardComp = board.getComponent(GbzBoard)
        //     const joints = boardComp.getJoints()
        //     joints.forEach(joint => {
        //         if (joint.node.active) {
        //             const group = joint.node.group
        //             const groupDataArr = levelData['boards'][group]
        //             if (groupDataArr && groupDataArr.length) {
        //                 const anchor = joint.anchor
        //                 const pos = toXY(joint.node, screws, anchor)
        //                 const screw = GbzPoolManager.ins.getNode('GbzScrew', screws, cc.v3(pos.x, pos.y))
        //                 const screwComp = screw.getComponent(GbzScrew)
        //                 const randomIndex = random(0, groupDataArr.length - 1)
        //                 const groudData = groupDataArr[randomIndex]
        //                 groupDataArr.splice(randomIndex, 1)
        //                 screwComp.init(groudData, group, joint)
        //                 joint.connectedBody = screw.getComponent(cc.RigidBody)
        //                 this.screws.push(screw)
        //             }
        //         }
        //     })
        //     this.boards.push(board)
        // })
        // })

        this.boxDataArr = levelData['boxes']
        this.rootBoxes.children.forEach(box => {
            const lock = box.getChildByName('lock')
            if (lock) {
                if (!lock.hasEventListener('click')) lock.on('click', this.onBoxLockClick, this)
            } else {
                const boxComp = box.getComponent(GbzBox)
                const index = this.boxDataArr.shift()
                boxComp.refresh(index)
            }
        })

        if (!this.node.hasEventListener('touchstart')) this.node.on('touchstart', this.onTouchStart, this)
    }

    initData(data: any) {
        let nums = 0
        for (let i = 0; i < data['groupData'].length; i++) {
            nums += data['groupData'][i]
        }
        let boxArr = []
        for (let i = 0; i < Math.floor(nums / 3); i++) {
            boxArr.push(random(1, 7))
        }
        let boardArr = []
        for (let i = 0; i < boxArr.length; i++) {
            const box = boxArr[i]
            boardArr.push(box, box, box)
        }
        data['boxes'] = boxArr
        for (let i = 0; i < data['groupData'].length; i++) {
            const num = data['groupData'][i]
            let arr = []
            for (let j = 0; j < num; j++) {
                const rand = random(1, 2)
                let v = rand == 1 ? boardArr.pop() : boardArr.shift()
                arr.push(v)
            }
            data['boards'][`g${i + 1}`] = arr
        }
    }

    testData() {
        const json = GbzResManager.ins.json(`gbz_level${GbzDataManager.ins.game_level_current}`)
        let data = []
        for (let key in json.boards) {
            data.push(...json.boards[key])
        }
        const obj1 = countGameLevelData(data)
        const obj2 = countGameLevelData(json.boxes)
        console.log(obj1, obj2)
    }

    onTouchStart(e: cc.Event.EventTouch) {
        if (GbzDataManager.ins.status == ENUM_STATUS.UNRUN) return
        const location = e.getLocation()
        if (GbzDataManager.ins.isSkill) {
            if (GbzDataManager.ins.skillIndex !== 2) return
            let targetBoard = null
            for (let i = this.boards.length - 1; i >= 0; i--) {
                const board = this.boards[i]
                const point = board.convertToNodeSpaceAR(location)
                const points = board.getComponent(cc.PolygonCollider).points
                const isInter = cc.Intersection.pointInPolygon(point, points)
                if (isInter) {
                    targetBoard = board
                    break
                }
            }
            if (targetBoard) {
                GbzAudioManager.ins.play(ENUM_CLIP.SKILL_REMOVE)
                cc.tween(targetBoard).to(0.2, { scale: 0 }).call(() => {
                    GbzDataManager.ins.isSkill = false
                    this.onBoardDestroy(targetBoard)
                }).start()
            }
        } else {
            let targetScrew = null
            for (let i = this.screws.length - 1; i >= 0; i--) {
                const screw = this.screws[i]
                const point = screw.convertToNodeSpaceAR(location)
                const points = screw.getComponent(cc.PolygonCollider).points
                const isInter = cc.Intersection.pointInPolygon(point, points)
                if (isInter) {
                    targetScrew = screw
                    break
                }
            }
            if (targetScrew) {
                const bool = this.checkScrewMove(targetScrew)
                if (bool) {
                    GbzAudioManager.ins.play(ENUM_CLIP.SCREW_OUT)
                    this.checkScrewMoveTo(targetScrew)
                } else {
                    GbzAudioManager.ins.play(ENUM_CLIP.SCREW_EMPTY)
                }
            }
        }
    }

    onScrewDestroy(node: cc.Node) {
        const index = this.screws.findIndex(screw => screw == node)
        if (index >= 0) this.screws.splice(index, 1)
        node.removeFromParent(true)
    }

    onBoardDestroy(node: cc.Node) {
        const index = this.boards.findIndex(board => board == node)
        if (index >= 0) this.boards.splice(index, 1)
        node.removeFromParent(true)
    }

    checkScrewMove(node: cc.Node) {
        let bool = true
        const screwComp = node.getComponent(GbzScrew)
        const screwGroupIndex = getNumFromStr(screwComp.group)
        for (let i = 0; i < this.boards.length; i++) {
            const board = this.boards[i]
            const pos = toXY(node, board)
            const points = board.getComponent(cc.PolygonCollider).points
            const isInter = cc.Intersection.polygonCircle(points, { position: pos, radius: node.width / 2 })
            if (isInter) {
                const boardGroupIndex = getNumFromStr(board.group)
                if (screwGroupIndex > boardGroupIndex) {
                    bool = false
                    break
                }
            }
        }
        return bool
    }

    checkScrewMoveTo(node: cc.Node) {
        const screwComp = node.getComponent(GbzScrew)
        let boxComp = null
        for (let i = 0; i < this.rootBoxes.children.length; i++) {
            const box = this.rootBoxes.children[i]
            const bcomp = box.getComponent(GbzBox)
            if (bcomp.isActive() && bcomp.index == screwComp.index) {
                boxComp = bcomp
                break
            }
        }

        if (boxComp) {
            const screwTarget = boxComp.getScrewCurrent()
            if (screwTarget) {
                const screwMoveNode = GbzPoolManager.ins.getNode('GbzScrewMove', this.node)
                const start = toXY(node, screwMoveNode.parent)
                const end = toXY(screwTarget, screwMoveNode.parent)
                this.onScrewDestroy(node)
                boxComp.addScrewNum()
                this.onScrewMove(screwMoveNode, screwComp.index, start, end, () => {
                    GbzAudioManager.ins.play(ENUM_CLIP.SCREW_IN)
                    screwTarget.active = true
                    const isFull = boxComp.checkFull()
                    if (isFull) {
                        this.onBoxRefresh(boxComp.node)
                    }
                })
            }
        } else {
            const targetComp = this.targets.getComponent(GbzTarget)
            if (targetComp.isActive()) {
                const screwTarget = targetComp.getScrewCurrent()
                if (screwTarget) {
                    const screwMoveNode = GbzPoolManager.ins.getNode('GbzScrewMove', this.node)
                    const start = toXY(node, screwMoveNode.parent)
                    const end = toXY(screwTarget, screwMoveNode.parent)
                    this.onScrewDestroy(node)
                    targetComp.pushScrew(screwComp.index)
                    this.onScrewMove(screwMoveNode, screwComp.index, start, end, () => {
                        screwTarget.active = true
                        targetComp.addScrew(screwComp.index, screwTarget)
                        const isFull = targetComp.checkFull()
                        if (isFull) GbzInstance.gameMgr.onGameOver(ENUM_UI.LAYER_LOSE_FULL)
                    })
                }
            } else {
                GbzAudioManager.ins.play(ENUM_CLIP.SCREW_EMPTY)
            }
        }
    }

    onTargetToBox(box: cc.Node) {
        const boxComp = box.getComponent(GbzBox)
        const targetComp = this.targets.getComponent(GbzTarget)
        const targetScrews = targetComp.getScrews()
        for (let i = 0; i < targetScrews.length; i++) {
            const screw = targetScrews[i]
            const index = getNumFromStr((screw.getComponent(cc.Sprite).spriteFrame)._name)
            if (boxComp.index == index) {
                if (boxComp.screwNum <= boxComp.screwNumMax - 1) {
                    const screwTarget = boxComp.getScrewCurrent()
                    if (screwTarget) {
                        const screwMoveNode = GbzPoolManager.ins.getNode('GbzScrewMove', this.node)
                        const start = toXY(screw, screwMoveNode.parent)
                        const end = toXY(screwTarget, screwMoveNode.parent)
                        targetComp.clearOne(screw)
                        boxComp.addScrewNum()
                        this.onScrewMove(screwMoveNode, index, start, end, () => {
                            GbzAudioManager.ins.play(ENUM_CLIP.SCREW_IN)
                            screwTarget.active = true
                            const isFull = boxComp.checkFull()
                            if (isFull) {
                                this.onBoxRefresh(boxComp.node)
                            }
                        })
                    }
                }
            }
        }
    }


    onScrewMove(node: cc.Node, index: number, start: cc.Vec2, end: cc.Vec2, cb?: () => void) {
        node.getComponent(cc.Sprite).spriteFrame = GbzResManager.ins.sprite(`gbz_screw${index}`)
        node.setPosition(start)
        node.active = true
        const moveDis = getDistance(start, end)
        const moveSpeed = 3000
        const moveTime = moveDis / moveSpeed
        cc.tween(node).to(moveTime, { position: cc.v3(end.x, end.y) }).call(() => {
            node.removeFromParent(true)
            cb && cb()
        }).start()
    }

    getTargetLock() {
        const comp = this.targets.getComponent(GbzTarget)
        return comp.getLock()
    }

    hasTargetScew() {
        const comp = this.targets.getComponent(GbzTarget)
        return comp.hasScrew()
    }

    getTargetScrews() {
        const comp = this.targets.getComponent(GbzTarget)
        return comp.getScrews()
    }

    onTargetClear() {
        const comp = this.targets.getComponent(GbzTarget)
        comp.clearAll()
    }

    checkTargetFull() {
        const targetComp = this.targets.getComponent(GbzTarget)
        return targetComp.checkFull()
    }

    onTargetLockDestroy(node: cc.Node) {
        const comp = this.targets.getComponent(GbzTarget)
        comp.addUnlockNum()
        node.removeFromParent(true)
    }

    onBoxLockClick(e: cc.Button) {
        if (GbzDataManager.ins.status == ENUM_STATUS.UNRUN) return
        GbzSdkManager.ins.showVideo(async () => {
            GbzAudioManager.ins.play(ENUM_CLIP.SKILL_UNLOCK)
            const lock = e.node
            this.onBoxRefresh(lock.parent)
        }, (msg: string) => {
            GbzToastManager.ins.show(msg, { gravity: 'TOP', bg_color: cc.color(226, 69, 109, 255) })
        })
    }

    onBoxRefresh(box: cc.Node) {
        const boxComp = box.getComponent(GbzBox)
        if (this.boxDataArr.length) {
            const boxIndex = this.boxDataArr[0]
            const res = takeValueFromArray(this.screwRefreshArr, boxIndex)
            if (res.length >= boxComp.screwNumMax) this.boxDataArr.shift()
            cc.tween(box).to(0.2, { scale: 0 }).call(() => {
                const index = this.boxDataArr.shift()
                boxComp.unlock()
                boxComp.setScrewNum(res.length)
                boxComp.refresh(index)
            }).to(0.2, { scale: 1 }).call(() => {
                this.onTargetToBox(box)
            }).start()
        } else {
            cc.tween(box).to(0.2, { scale: 0 }).call(() => {
                boxComp.unlock()
                if (this.checkBoxClear()) {
                    GbzInstance.gameMgr.onGameOver(ENUM_UI.LAYER_WIN)
                }
            }).start()
        }
    }

    checkBoxClear() {
        let count = 0
        this.rootBoxes.children.forEach(box => {
            const boxComp = box.getComponent(GbzBox)
            if (boxComp.isActive()) {
                count += 1
            }
        })
        return count <= 0
    }
}
